﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using CloudShot.Core.Interfaces;
using CloudShot.Interfaces;
using CloudShot.Utils;

namespace CloudShot.Services
{
  internal class PluginManager : IPluginManager
  {
    private readonly string _pluginsRoot;

    public PluginManager(string pluginsRoot)
    {
      _pluginsRoot = pluginsRoot;
      AppDomain.CurrentDomain.AssemblyResolve += OnAssemblyResolve;
    }

    public IEnumerable<IImageStorage> GetPlugins()
    {
      if (!Directory.Exists(_pluginsRoot))
        yield break;

      string[] directories = Directory.GetDirectories(_pluginsRoot);
      var assemblies = new List<Assembly>();

      foreach (string directory in directories)
      {
        string[] files = Directory.GetFiles(directory, "*.dll", SearchOption.TopDirectoryOnly);

        foreach (string file in files)
        {
          Assembly assembly;

          if (TryLoadAssembly(file, out assembly))
            assemblies.Add(assembly);
        }
      }

      foreach (var assembly in assemblies)
      {
        IEnumerable<Type> storageTypes = assembly.FindDerivedTypesFromAssembly(typeof(IImageStorage));

        foreach (Type storageType in storageTypes)
        {
          var storage = Activator.CreateInstance(storageType) as IImageStorage;
          yield return storage;
        }
      }
    }

    private bool TryLoadAssembly(string fileName, out Assembly assembly)
    {
      try
      {
        assembly = Assembly.LoadFile(fileName);
        return true;
      }
      catch
      {
        assembly = null;
        return false;
      }
    }

    private Assembly OnAssemblyResolve(object sender, ResolveEventArgs args)
    {
      Assembly assembly = null;
      Assembly[] myAssemblies = AppDomain.CurrentDomain.GetAssemblies();

      foreach (Assembly myAssembly in myAssemblies)
      {
        if (args.Name == myAssembly.FullName)
        {
          assembly = myAssembly;
          break;
        }
      }

      return assembly;
    }
  }
}